package storage

import (
	"bytes"
	"github.com/boltdb/bolt"
)

type boltMetaEngine struct {
	db *bolt.DB
}

type item struct {
	key   []byte
	value []byte
}

func newBoltMetaEngine(db *bolt.DB) boltMetaEngine {
	db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte("boltMetaBucket"))
		if err != nil {
			return err
		}
		return nil
	})
	return boltMetaEngine{
		db,
	}
}

func (e *boltMetaEngine) Set(key, value []byte) error {
	e.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltMetaBucket"))
		err := b.Put(key, value)
		if err != nil {
			return err
		}
		return nil
	})
	return nil
}

func (e *boltMetaEngine) Get(key []byte) ([]byte, error) {
	var v []byte

	e.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltMetaBucket"))
		v = b.Get(key)
		return nil
	})

	return v, nil
}

func (e *boltMetaEngine) Delete(key []byte) error {
	return e.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltMetaBucket"))
		return b.Delete(key)
	})
}

func (e *boltMetaEngine) RangeDelete(start, end []byte) error {
	e.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltMetaBucket"))
		c := b.Cursor()
		for k, _ := c.Seek(start); k != nil && bytes.Compare(k, end) <= 0; k, _ = c.Next() {
			err := b.Delete(k)
			if err != nil {
				return err
			}
		}
		return nil
	})
	return nil
}

func (e *boltMetaEngine) Scan(start, end []byte, handler func(key, value []byte) (bool, error), pooledKey bool) error {
	var items []*item
	e.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltMetaBucket"))
		c := b.Cursor()
		for k, v := c.Seek(start); k != nil && bytes.Compare(k, end) <= 0; k, v = c.Next() {
			items = append(items, &item{k, v})
		}
		return nil
	})

	for _, target := range items {
		c, err := handler(target.key, target.value)
		if err != nil || !c {
			return err
		}
	}
	return nil
}

func (e *boltMetaEngine) Free(unsafe []byte)  {

}

func (e *boltMetaEngine) Seek(key []byte) ([]byte, []byte, error) {
	var k, v []byte
	err := e.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("boltMetaBucket"))
		c := b.Cursor()
		k, v = c.Seek(key)
		k, v = c.Next()
		return nil
	})
	if err != nil || k == nil || v == nil {
		return nil, nil, err
	}
	return k, v, nil
}